package com.ssd.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;

import com.ssd.api.payment.dto.enums.TradingStateEnum;
import com.ssd.api.payment.dto.msg.TradeStatusMsg;
import com.ssd.exception.LogicException;
import com.ssd.constant.TradingConstant;
import com.ssd.handler.alipay.AlipayConfig;
import com.ssd.handler.wechat.WechatPayHttpClient;
import com.ssd.mixly.service.IMqttService;
import com.ssd.mixly.service.impl.MqttServiceImpl;
import com.ssd.model.domain.Trading;
import com.ssd.service.NotifyService;
import com.ssd.service.TradingService;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付成功的通知处理
 *
 * @author itcast
 */
@Slf4j
@Service
public class NotifyServiceImpl implements NotifyService {

    /**
     * 注入交易服务，用于处理与交易相关的操作。
     */
    @Resource
    private TradingService tradingService;

    /**
     * 注入Redis客户端，用于分布式锁管理和其他功能。
     */
    @Resource
    private RedissonClient redissonClient;

    /**
     * 注入RabbitMQ客户端，用于消息队列操作，如发送和接收消息。
     */
//    @Resource todo
//    private RabbitClient rabbitClient;


    /**
     * 处理微信支付通知的方法
     * 当微信支付成功或有其他状态变化时，微信会向系统发送通知，此方法用于处理这些通知
     *
     * @param request      包含通知请求数据的对象
     * @param enterpriseId 企业ID，用于获取对应的微信支付配置
     * @throws LogicException 如果处理通知过程中发生错误，抛出此异常
     */
    @Override
    public void wxPayNotify(NotificationRequest request, Long enterpriseId) throws LogicException {
        // 查询配置
        WechatPayHttpClient client = WechatPayHttpClient.get(enterpriseId);

        JSONObject jsonData;

        //验证签名，确保请求来自微信
        try {
            //确保在管理器中存在自动更新的商户证书
            client.createHttpClient();

            // 获取证书管理器实例，用于统一管理和验证数字证书
            CertificatesManager certificatesManager = CertificatesManager.getInstance();

            // 根据商户ID获取相应的验证器，用于验证商户身份或签名有效性
            Verifier verifier = certificatesManager.getVerifier(client.getMchId());
            //验签和解析请求数据
            NotificationHandler notificationHandler = new NotificationHandler(verifier, client.getApiV3Key().getBytes(StandardCharsets.UTF_8));
            Notification notification = notificationHandler.parse(request);
            //通知的类型，支付成功通知的类型为TRANSACTION.SUCCESS，只处理此类通知
            if (!StrUtil.equals("TRANSACTION.SUCCESS", notification.getEventType())) {
                return;
            }

            //获取解密后的数据
            jsonData = JSONUtil.parseObj(notification.getDecryptData());
        } catch (Exception e) {
            throw new LogicException( "验签失败");
        }

        //交易状态
        String tradeStateFromWX = jsonData.getStr("trade_state");
        /**
         交易状态，枚举值：
         SUCCESS：支付成功
         REFUND：转入退款
         NOTPAY：未支付
         CLOSED：已关闭
         REVOKED：已撤销（付款码支付）
         USERPAYING：用户支付中（付款码支付）
         PAYERROR：支付失败(其他原因，如银行返回失败)
         */
        String tradeStatus = TradingStateEnum.FKZ.getValue();
        //根据微信返回的交易状态，更新系统的交易状态
        if (StrUtil.equalsAny(tradeStateFromWX, TradingConstant.WECHAT_TRADE_CLOSED, TradingConstant.WECHAT_TRADE_REVOKED)) {
            tradeStatus = TradingStateEnum.QXDD.getValue();//取消订单
            //支付成功或转入退款的更新为已付款
        } else if (StrUtil.equalsAny(tradeStateFromWX, TradingConstant.WECHAT_TRADE_SUCCESS, TradingConstant.WECHAT_TRADE_REFUND)) {
            tradeStatus = TradingStateEnum.YJS.getValue();//已付款
        } else if (StrUtil.equalsAny(tradeStateFromWX, TradingConstant.WECHAT_TRADE_PAYERROR)) {
            tradeStatus = TradingStateEnum.FKSB.getValue();//付款失败
        }

        //交易单号
        Long tradingOrderNo = jsonData.getLong("out_trade_no");
        log.info("微信支付通知：tradingOrderNo = {}, data = {}", tradingOrderNo, jsonData);

        //更新交易单
        this.updateTrading(tradingOrderNo, jsonData.getStr("transaction_id"), tradeStatus, jsonData.getStr("trade_state_desc"), jsonData.toString());
    }

    /**
     * 更新交易信息
     *
     * @param tradingOrderNo 交易订单编号
     * @param transactionId  交易流水号
     * @param tradeStatus    交易状态
     * @param resultMsg      结果消息
     * @param resultJson     结果JSON
     *                       <p>
     *                       此方法用于更新交易状态和其他相关信息当接收到支付通知时调用
     *                       它不仅更新数据库中的交易记录，还发送消息通知其他系统关于交易状态的变更
     */
    private void updateTrading(Long tradingOrderNo, String transactionId, String tradeStatus, String resultMsg, String resultJson) {
        try {
            // 根据交易订单号查找交易记录
            Trading trading = this.tradingService.findTradByTradingOrderNo(tradingOrderNo);
            TradingStateEnum tradingStateEnum = TradingStateEnum.fromValue(tradeStatus);
            // 更新交易状态
            trading.setTradingState(tradingStateEnum);
            // 清空二维码数据
            trading.setQrCode("");
            // 更新交易流水号
            trading.setTransactionId(transactionId);//第三方支付的交易号
            // 更新结果消息
            trading.setResultMsg(resultMsg);
            // 更新结果JSON
            trading.setResultJson(resultJson);
            // 保存或更新交易记录
            this.tradingService.saveOrUpdate(trading);

            // 构建交易状态消息对象 todo 到时候用emq发消息给订单服务
            TradeStatusMsg tradeStatusMsg = TradeStatusMsg.builder()
                    .tradingOrderNo(trading.getTradingOrderNo())
                    .productOrderNo(trading.getProductOrderNo())
                    .productAppId(trading.getProductAppId())
                    .transactionId(trading.getTransactionId())
                    .tradingChannel(trading.getTradingChannel())
                    .statusCode(TradingStateEnum.YJS.getCode())
                    .statusName(TradingStateEnum.YJS.name())
                    .info(trading.getMemo())// 备注信息
                    .build();

            // 将交易状态消息转换为JSON字符串
            String msg = JSONUtil.toJsonStr(tradeStatusMsg);

            // 发送消息到 mixly，通知其他系统
            IMqttService service = new MqttServiceImpl();
            service.send("pay", msg);

        } catch (Exception e) {
            System.out.println();
            // 如果发生异常，抛出自定义异常
            log.error( "处理业务失败", e.getMessage());
            System.out.println();
            throw new LogicException( "处理业务失败"+e.getMessage());
        }
    }

    /**
     * 处理支付宝通知
     * 当支付宝服务器发送交易通知时，本方法负责接收和处理这些通知
     * 主要功能包括验证通知的合法性，根据交易状态更新订单状态
     *
     * @param request      HTTP请求对象，包含支付宝通知的参数
     * @param enterpriseId 企业ID，用于获取支付宝配置信息
     * @throws LogicException 如果验签失败或更新订单状态时发生错误，抛出此异常
     */
    @Override
    public void aliPayNotify(HttpServletRequest request, Long enterpriseId) throws LogicException {
        //获取参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, String> param = new HashMap<>();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            param.put(entry.getKey(), StrUtil.join(",", entry.getValue()));
        }

        //根据支付宝通知的交易状态，确定内部交易状态
        String tradeStatusFromAli = param.get("trade_status");
        String tradeStatus = TradingStateEnum.FKZ.getValue();
        if (StrUtil.equals(TradingConstant.ALI_TRADE_CLOSED, tradeStatusFromAli)) {
            //支付取消：TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
            tradeStatus = TradingStateEnum.QXDD.getValue();
        } else if (StrUtil.equalsAny(tradeStatusFromAli, TradingConstant.ALI_TRADE_SUCCESS, TradingConstant.ALI_TRADE_FINISHED)) {
            // TRADE_SUCCESS（交易支付成功）
            // TRADE_FINISHED（交易结束，不可退款）
            tradeStatus = TradingStateEnum.YJS.getValue();
        }

        //查询配置
        Config config = AlipayConfig.getConfig(enterpriseId);
        Factory.setOptions(config);
        try {
            //验证支付宝通知的合法性
            Boolean result = Factory
                    .Payment
                    .Common().verifyNotify(param);
            if (!result) {
                throw new LogicException( "验签失败");
            }
        } catch (Exception e) {
            throw new LogicException( "验签失败");
        }

        //获取交易单号
        Long tradingOrderNo = Convert.toLong(param.get("out_trade_no"));
        String transactionId = param.get("trade_no");
        //更新交易单
        this.updateTrading(tradingOrderNo, transactionId, tradeStatus, "", JSONUtil.toJsonStr(param));
    }
}
